[ 狀況題 ] 如何徹底將檔案從 Git 中移除?


Posted by krebikshaw on 2020-08-09

為何會寫這篇文章?

程式導師計畫第四期 的課程,到了 week9 的時候,需要開始練習如何利用 php 來串接資料庫。由於連線資料庫的過程,是需要透過帳號及密碼來連線的。所以至少會有一份 php 檔案當中會寫有你的資料庫的帳號跟密碼,也就是我們所謂的「敏感資訊」。在 week9 的作業中,這份檔案就是 conn.php

其實這個錯誤不會發生的

  • Huli 有在「如何上傳作業」說明中,提到了他已經幫我們將 conn.php 這份會帶有敏感資訊的檔案給加入了 .gitignore 當中。(Huli 已經貼心的幫我們設定好了)

    正常的情況下,在各位 commit 的時候,conn.php 這份檔案是不會被加入版本控制的

但它偏偏還是發生了

  • 當我交完作業之後發現,咦?這份檔案怎麼傳不上去,裡面有程式碼唉,助教看不到這份檔案,不就沒辦法幫我裡面寫的內容了嗎? ( 聰明的你應該已經發現我當時已經把老師的警告都忘記啦! )
  • 我很天真的認為應該要把檔案放上去讓助教可以改作業,我打開了我的瀏覽器,連結至 GitHub 的 repository 直接把那份檔案用滑鼠拉進來雲端的 repository 裡面!

說時遲那時快

機警的 Huli 在第一時間發現了問題,立馬來關心這位迷糊的同學!

Huli:
你的 conn.php 沒有被 git ignore 掉嗎?
因為照理來說應該會被弄掉才對
我有放到 .gitignore 裡面
接下來要請你試著把那個檔案完全拿掉了
直接刪掉是沒用的 因為還是留在紀錄裡

  • 所以這篇文章就誕生啦

不小心把帳號密碼放在 Git 裡了,想把它刪掉…

若你看過 Z 同學先前分享過的這篇文章「刪除了 branch 將 commit 資料也刪除了~怎麼辦!!!」,你就會察覺,加入版本控制的檔案,一但刪除是可以有機會救回來的,所以要把檔案刪掉,就必須把所有能救回來的路線跟方法通通斬斷。

刪除檔案前置動作: 首要任務

  • 若不小心把帳號密碼放在 Git 的首要任務,就是在刪除檔案之前先趕快換掉你的密碼!
  • 確保密碼重設之後,再來次要任務就是刪除這份被加進 Git 的檔案了

方法一: 把 .git 刪除

  • 若這份專案是你的個人專案,你不在乎以前 commit 的紀錄,而且這份專案從來沒有 push 到倉庫過,那其實只要把 .git 目錄刪掉就可以了因為所有的 Commit 紀錄都在 .git 目錄裡。

  • 但若是這份專案的 .git 是不能刪掉的,或者已經把 push 到倉庫上,那只能參考「方法二」把檔案全面修改了。

方法二: 把所有版本當中的 conn.php 連根拔除

  • Z 同學分享的這篇文章「 刪除了 branch 將 commit 資料也刪除了~怎麼辦!!!」 提到刪除的 branch 是可以救回來的,也就是說,你刪掉任何東西,通通可以反悔。( 我刪除了,又反悔了,我又刪除了,我又反悔了! )
  • 那這樣要如何操作呢?

步驟 1:使用 filter-branch 指令

  • git filter-branch --tree-filter "rm -f conn.php"
  • 我們都知道使用 rm 可以刪除掉檔案
  • 但是因為所有 commit 過的版本,只要裡頭有記錄到 conn.php 這個檔案的都要刪掉
  • 所以 Git 有一個叫做 filter-branch 的指令,它可以一次大量的修改 Commit。
    • filter-branch 這個指令可以根據不同的 filter,一個一個 Commit 的來處理它。
    • --tree-filter 可以在 Checkout 到每個 Commit 的時候執行接下來的指令,執行完後再自動幫你重新再 Commit。
    • 以下面的例子來說,便是執行「強制刪除 conn.php 檔案」並在重新 commit 一次。
$ git filter-branch --tree-filter "rm -f conn.php"

WARNING: git-filter-branch has a glut of gotchas generating mangled history
     rewrites.  Hit Ctrl-C before proceeding to abort, then use an
     alternative filtering tool such as 'git filter-repo'
     (https://github.com/newren/git-filter-repo/) instead.  See the
     filter-branch manual page for more details; to squelch this warning,
     set FILTER_BRANCH_SQUELCH_WARNING=1.
Proceeding with filter-branch...

You need to run this command from the toplevel of the working tree.
  • 它會跟你說,你必須要先切換到專案最上層的資料夾,才能下這個指令

    • 也就是 mentor-program-4th 這個資料夾
  • 所以我們切換到正確路徑之後,再下一次指令

$ git filter-branch --tree-filter "rm -f conn.php"

WARNING: git-filter-branch has a glut of gotchas generating mangled history
     rewrites.  Hit Ctrl-C before proceeding to abort, then use an
     alternative filtering tool such as 'git filter-repo'
     (https://github.com/newren/git-filter-repo/) instead.  See the
     filter-branch manual page for more details; to squelch this warning,
     set FILTER_BRANCH_SQUELCH_WARNING=1.
Proceeding with filter-branch...

Rewrite de410b35495fe34dbd5763bc31195ea6b2065e97 (107/109) (11 seconds passed, remaining 0 predicted)
Ref 'refs/heads/master' was rewritten
  • 這樣所有 commit 的紀錄中,conn.php 這份檔案就通通拿掉了

步驟 2:再重頭來一次

  • git filter-branch -f --tree-filter "rm -f conn.php"
  • 剛剛刪除掉都是假的,因為你隨時可以反悔
  • 所以為了要把 filter-branch 的備份點蓋過去,我們要在剛剛的指令中加入一個 -f,強制覆寫 filter-branch 的備份點
$ git filter-branch -f --tree-filter "rm -f conn.php"

WARNING: git-filter-branch has a glut of gotchas generating mangled history
     rewrites.  Hit Ctrl-C before proceeding to abort, then use an
     alternative filtering tool such as 'git filter-repo'
     (https://github.com/newren/git-filter-repo/) instead.  See the
     filter-branch manual page for more details; to squelch this warning,
     set FILTER_BRANCH_SQUELCH_WARNING=1.
Proceeding with filter-branch...

Rewrite f63bdc1d284d649a2fde6474873be812b6715daa (101/109) (9 seconds passed, remaining 0 predicted)
Ref 'refs/heads/master' was rewritten

步驟 3:製造不在場證明

  • rm .git/refs/original/refs/heads/master
  • 由於剛剛執行 filter-branch 指令的「動作」也會被儲存下來,隨時可以透過它再跳回去,所以要把這份紀錄也刪掉
$ rm .git/refs/original/refs/heads/master

步驟 4:把 Reflog 清掉

  • $ git reflog expire --all --expire=now
  • Z 同學就是利用 Reflog 來把刪除的 branch 救回來的,所以這份 Reflog 我們要立刻讓他過期
$ git reflog expire --all --expire=now

步驟 5:git fsck

  • $ git fsck --unreachable
  • 這行指令我沒能很明白其中的意思,說明上是寫能印出存在但是不能從任何參考節點到達的對象。
    • 這裡指的應該是查看狀態為 unreachable 的這些物件
$ git fsck --unreachable

Checking object directories: 100% (256/256), done.
Checking objects: 100% (410/410), done.
unreachable tree 21f053d4331219ed36df3defc62f813a0a3bdcc4
unreachable tree 27e052e5194d4093c0717b2ff638db039198dfa4
unreachable tree 30004f2209bb3181a14aca171587f8834c319149
unreachable tree 5060b7e072951cda0e6299a58059d7c1a5ca4c1b
unreachable blob 7ee0adb09094d06384a33d9da509b30ba02b6128
unreachable tree 9240fa4cf76000df5cb65c2519cff724242a52ff
unreachable tree a33066762b73fd6ee9d0b30e1a23b34120dc8f4a
.
.
.

步驟 6:啟動 Git 回收機制

  • $ git gc --prune=now
  • git gc 可以清理不必要的文件並優化電腦的記憶體空間
  • --prune=now 修剪早於現在的所有鬆散對象
  • 這段指令白話文就是把剛剛印出的這些 unreachable 的物件通通回收掉
$ git gc --prune=now

Enumerating objects: 1073, done.
Counting objects: 100% (1073/1073), done.
Delta compression using up to 4 threads
Compressing objects: 100% (893/893), done.
Writing objects: 100% (1073/1073), done.
Total 1073 (delta 498), reused 382 (delta 128)
Computing commit graph generation numbers: 100% (213/213), done.
  • 再下一次剛剛的 git fsck 可以發現這些物件都回收掉了
$  git fsck

Checking object directories: 100% (256/256), done.
Checking objects: 100% (1073/1073), done.
  • 目前為止,應該已經沒辦法找回原來的 SHA-1 值了

步驟 7:同步倉庫的紀錄

  • git push -f
  • 本地端處理完後,最後就是要把雲端的資料也修正過來了
  • git push -f 可以把線上的紀錄覆蓋掉
$  git push -f

Enumerating objects: 855, done.
Counting objects: 100% (773/773), done.
Delta compression using up to 4 threads
Compressing objects: 100% (363/363), done.
Writing objects: 100% (703/703), 8.16 MiB | 106.00 KiB/s, done.
Total 703 (delta 331), reused 622 (delta 320)
remote: Resolving deltas: 100% (331/331), completed with 12 local objects.
remote:
remote: GitHub found 10 vulnerabilities on Lidemy/mentor-program-4th-krebikshaw's default branch (2 critical, 3 high, 4 moderate, 1 low). To find out more, visit:
remote:      https://github.com/Lidemy/mentor-program-4th-krebikshaw/network/alerts
remote:
To https://github.com/Lidemy/mentor-program-4th-krebikshaw.git
 + de410b3...f63bdc1 master -> master (forced update)

  • 從 GitHub 上的的資料,可以看到有 103 筆的 commit 被重新修正了

結語

大家真的在繳交作業的時候需要注意,帶有敏感資訊的檔案,不可以加入版本控制。由於 Huli 幫我們加進 .gitignore 裡的是 conn.php 這份檔案,所以若是你的帳號密碼不是寫在這份檔案裡面,一但 push 上雲端後要修正是件浩大的工程。

避免的方法就是,乖乖的只把帳號密碼寫在 conn.php 裡面,並且不要沒事去把檔案拉上去 repository。另外一種狀況是,你如果有把帳號密碼寫在其他檔案的,就應該馬上先把檔案加入 .gitignore 裡面。

此篇文章是我自己處理過程的紀錄,程式碼是參考這篇 Huli 給我的文章 「【狀況題】不小心把帳號密碼放在 Git 裡了,想把它刪掉…」來操作的。










Related Posts

[ js 筆記 ] 閉包 Closure

[ js 筆記 ] 閉包 Closure

短網址系統設計

短網址系統設計

[AI人工智能] 二、安裝python虛擬環境 + pytorch

[AI人工智能] 二、安裝python虛擬環境 + pytorch


Comments